package com.an.blog.strategy.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.an.blog.admin.domain.BlogArticle;
import com.an.blog.admin.domain.BlogCategory;
import com.an.blog.admin.domain.BlogTag;
import com.an.blog.mapper.BlogArticleMapper;
import com.an.blog.mapper.BlogCategoryMapper;
import com.an.blog.mapper.BlogTagMapper;
import com.an.blog.service.IBlogCommentService;
import com.an.blog.strategy.SearchStrategy;
import com.an.blog.third.es.domain.EsBlogArticle;
import com.an.blog.third.es.esmapper.EsBlogArticleMapper;
import com.an.blog.utils.BlogUniversalUtil;
import com.an.blog.web.domain.bo.BlogArticleWebBo;
import com.an.blog.web.domain.bo.BlogCommentQueryWebBo;
import com.an.blog.web.domain.vo.BlogArticleDetailWebVo;
import com.an.blog.web.domain.vo.BlogArticleWebVo;
import com.an.blog.web.domain.vo.BlogCommentWebVo;
import com.an.common.constant.BlogConstants;
import com.an.common.constant.CacheConstants;
import com.an.common.core.domain.PageQuery;
import com.an.common.core.page.TableDataInfo;
import com.an.common.utils.StreamUtils;
import com.an.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.dromara.easyes.core.biz.EsPageInfo;
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * ES 搜索实现
 *
 * @author Ann
 * @date 2024/07/08
 */
@Service("EsSearchImpl")
@RequiredArgsConstructor
public class EsSearchImpl implements SearchStrategy {

    private final EsBlogArticleMapper esBlogArticleMapper;

    private final BlogTagMapper tagMapper;

    private final BlogArticleMapper articleMapper;

    private final BlogCategoryMapper categoryMapper;

    private final IBlogCommentService commentService;

    /**
     * 查询网页列表
     *
     * @param articleWebBo 文章 web bo
     * @param pageQuery    页面查询
     * @author Ann
     * @date 2024/07/25
     */
    @Override
    public TableDataInfo<BlogArticleWebVo> queryWebPageList(BlogArticleWebBo articleWebBo, PageQuery pageQuery) {
        // 从 articleWebBo 中提取搜索参数
        String keyword = articleWebBo.getKeyword();
        Long[] tagIds = articleWebBo.getTagIds();
        Long categoryId = articleWebBo.getCategoryId();
        List<String> tagNames = new ArrayList<>();
        String categoryName = null;
        // 如果提供了 tagIds，则获取对应的标签名称列表
        if (tagIds != null) {
            List<BlogTag> blogTags = tagMapper.selectList(new LambdaQueryWrapper<BlogTag>()
                .in(BlogTag::getTagId, articleWebBo.getTagIds()));
            tagNames = StreamUtils.toList(blogTags, BlogTag::getTagName);
        }
        // 如果提供了 categoryId，则获取对应的分类名称
        if (categoryId != null) {
            BlogCategory blogCategory = categoryMapper.selectById(categoryId);
            categoryName = blogCategory.getCategoryName();
        }

        // 构建用于 Elasticsearch 的查询包装器
        LambdaEsQueryWrapper<EsBlogArticle> wrapper = new LambdaEsQueryWrapper<EsBlogArticle>()
            .multiMatchQuery(StringUtils.isNotBlank(keyword), keyword, EsBlogArticle::getArticleTitle, EsBlogArticle::getArticleContent)
            .in(CollUtil.isNotEmpty(tagNames), EsBlogArticle::getTags, tagNames)
            .eq(StringUtils.isNotBlank(categoryName), EsBlogArticle::getCategoryName, categoryName)
            .eq(EsBlogArticle::getStatus, BlogConstants.NORMAL)
            .orderByDesc(EsBlogArticle::getIsTop, EsBlogArticle::getCreateTime);

        // 在 Elasticsearch 中查询分页结果
        EsPageInfo<EsBlogArticle> articleEsPageInfo = esBlogArticleMapper
            .pageQuery(wrapper, pageQuery.getPageNum(), pageQuery.getPageSize());
        List<EsBlogArticle> esBlogArticles = articleEsPageInfo.getList();

        // 如果未找到结果，则返回空的 TableDataInfo
        if (CollUtil.isEmpty(esBlogArticles)) {
            return TableDataInfo.build(Collections.emptyList());
        }
        // 提取 Elasticsearch 结果中的文章ID列表
        List<Long> articleIds = StreamUtils.toList(esBlogArticles, EsBlogArticle::getArticleId);

        // 根据文章ID从 MySQL 中查询额外的数据（点赞数和浏览数）
        List<BlogArticle> blogArticleList = articleMapper.selectList(new LambdaQueryWrapper<BlogArticle>()
            .select(BlogArticle::getArticleId, BlogArticle::getLikedCount, BlogArticle::getViewedCount)
            .in(CollUtil.isNotEmpty(articleIds), BlogArticle::getArticleId, articleIds));
        // 创建文章ID到点赞数和浏览数的映射
        Map<Long, Long> likedCountMap = StreamUtils.toMap(blogArticleList, BlogArticle::getArticleId,
            BlogArticle::getLikedCount);
        Map<Long, Long> viewedCountMap = StreamUtils.toMap(blogArticleList, BlogArticle::getArticleId,
            BlogArticle::getViewedCount);

        List<BlogArticleWebVo> blogArticleWebVos = esBlogArticles.stream().map(esBlogArticle -> {
            BlogArticleWebVo blogArticleWebVo = BeanUtil.toBean(esBlogArticle, BlogArticleWebVo.class);
            blogArticleWebVo.setLikedCount(likedCountMap.get(esBlogArticle.getArticleId()));
            blogArticleWebVo.setViewedCount(viewedCountMap.get(esBlogArticle.getArticleId()));
            //是否点赞了
            BlogUniversalUtil.isLiked(blogArticleWebVo, CacheConstants.ARTICLE_LIKED_KEY + blogArticleWebVo.getArticleId(),
                BlogArticleWebVo::setIsLiked);
            return blogArticleWebVo;
        }).collect(Collectors.toList());

        return TableDataInfo.build(blogArticleWebVos, articleEsPageInfo.getTotal());
    }

    @Override
    public BlogArticleDetailWebVo queryWebDetailById(Long articleId) {
        EsBlogArticle esBlogArticle = esBlogArticleMapper.selectById(articleId);
        //构建返回前端对象
        BlogArticleDetailWebVo blogArticleDetailWebVo = BeanUtil.toBean(esBlogArticle, BlogArticleDetailWebVo.class);
        //是否点赞了
        BlogUniversalUtil.isLiked(blogArticleDetailWebVo, CacheConstants.ARTICLE_LIKED_KEY + articleId,
            BlogArticleDetailWebVo::setIsLiked);
        // 根据文章ID从 MySQL 中查询额外的数据（点赞数和浏览数）
        BlogArticle blogArticle = articleMapper.selectOne(new LambdaQueryWrapper<BlogArticle>()
            .select(BlogArticle::getLikedCount, BlogArticle::getViewedCount)
            .eq(BlogArticle::getArticleId, articleId));
        blogArticleDetailWebVo.setLikedCount(blogArticle.getLikedCount());
        blogArticleDetailWebVo.setViewedCount(blogArticle.getViewedCount());
        Long commentCount = commentService.queryCountByTypeAndTopicId(BlogConstants.COMMENT_ARTICLE_TOPIC, articleId);
        blogArticleDetailWebVo.setCommentedCount(commentCount);
        if (commentCount > 0) {
            List<BlogCommentWebVo> blogCommentWebVos = queryArticleCommentList(articleId);
            blogArticleDetailWebVo.setBlogCommentWebVoList(blogCommentWebVos);
        }
        return blogArticleDetailWebVo;
    }

    /**
     * 查询文章评论列表
     *
     * @param articleId 文章编号
     * @return {@link List }<{@link BlogCommentWebVo }>
     * @author Ann
     * @date 2024/06/13
     */
    private List<BlogCommentWebVo> queryArticleCommentList(Long articleId) {
        BlogCommentQueryWebBo blogCommentQueryWebBo = new BlogCommentQueryWebBo();
        blogCommentQueryWebBo.setTopicId(articleId);
        blogCommentQueryWebBo.setTopic(BlogConstants.COMMENT_ARTICLE_TOPIC);
        return commentService.queryWebList(blogCommentQueryWebBo);
    }
}
